/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
import { Connection } from "../Connection"
import { Handshake } from "../Handshake"
import { Protocol } from "../Protocol"
import { Route } from "../Route"
import { RealConnectionPool } from "./RealConnectionPool"
import { connection, http, networkSecurity, socket } from "@kit.NetworkKit"
import { buffer, List } from "@kit.ArkTS"
import { RealCall } from "./RealCall"
import { Call } from "../Call"
import { EventListener } from "../EventListener"
import { ConnectionSpecSelector } from "./ConnectionSpecSelector"
import { HttpUrl } from "../HttpUrl"
import { Address } from "../Address"
import { OkHttpClient } from "../OkHttpClient"
import { ExchangeCodec } from "../http/ExchangeCodec"
import { Exchange } from "./Exchange"
import {
    CertificateException,
    ConnectionShutdownException,
    createSocketException,
    IOException, ProtocolException, RouteException,
    SocketErrorCode,
    SocketException,
    SSLPeerUnverifiedException,
    StreamResetException,
    UnknownServiceException} from "../Error/error"
import { EMPTY_RESPONSE, kotlinCheck, toHostHeader, userAgent } from "../utils/Utils"
import { ConnectionSpec } from "../ConnectionSpec"
import { ProxyType } from "../Socket/Proxy"
import { NetAddress, ProxyOptions, TCPConnectOptions } from "../Socket/TCPConnectOptions"
import { BufferedSource } from "../okio/BufferedSource"
import { BufferedSink } from "../okio/BufferedSink"
import { SSLSocket } from "../Socket/SSLSocket"
import { TLSConnectOptions } from "../Socket/TLSConnectOptions"
import { Http1ExchangeCodec } from "../http1/Http1ExchangeCodec"
import { Request } from '../Request'
import OkHostnameVerifier from "../tls/OkHostnameVerifier"
import { CertificatePinner } from "../CertificatePinner"
import cert from "@ohos.security.cert"
import { Response } from "../Response"
import { ErrorCode } from "../http2/ErrorCode"
import { BusinessError } from "@kit.BasicServicesKit"

export class RealConnection implements Connection { //Http2Connection.Listener(), 暂时忽略http2实现

    static NPE_THROW_WITH_NULL = "throw with null exception"
    static MAX_TUNNEL_ATTEMPTS = 21
    static IDLE_CONNECTION_HEALTHY_NS = 10_000_000_000 // 10 seconds in nanoseconds.

    connectionPool: RealConnectionPool
    private _route: Route

    constructor(connectionPool: RealConnectionPool, route: Route) {
        this.connectionPool = connectionPool
        this._route = route
    }

    // These properties are initialized by connect() and never reassigned.
    /** The low-level TCP socket. */
    private rawSocket: socket.TCPSocket | null = null

    /**
     * The application layer socket. Either an [SSLSocket] layered over [rawSocket], or [rawSocket]
     * itself if this connection does not use SSL.
     */
    private _socket: socket.TCPSocket | socket.TLSSocket | null = null
    private _handshake?: Handshake = null
    private _protocol?: Protocol = null
    private _socketClosed: boolean = false
    private http2Connection: null = null // Http2Connection | null
    private source?: BufferedSource = null
    private sink?: BufferedSink = null

    /**
     * 最后一个 Socket 错误
     * 当 socket 发生错误时存储错误信息，可用于诊断连接问题
     */
    private _lastSocketError: IOException | null = null

    /**
     * 获取最后一个 Socket 错误
     * @returns 最后一个 socket 错误，如果没有错误则返回 null
     */
    get lastSocketError(): IOException | null {
        return this._lastSocketError
    }

    // These properties are guarded by this.

    /**
     * If true, no new exchanges can be created on this connection. It is necessary to set this to
     * true when removing a connection from the pool; otherwise a racing caller might get it from the
     * pool when it shouldn't. Symmetrically, this must always be checked before returning a
     * connection from the pool.
     *
     * Once true this is always true. Guarded by this.
     */
    _noNewExchanges = false

    /**
     * If true, this connection may not be used for coalesced requests. These are requests that could
     * share the same connection without sharing the same hostname.
     */
    private _noCoalescedConnections = false

    /**
     * The number of times there was a problem establishing a stream that could be due to route
     * chosen. Guarded by this.
     */
    routeFailureCount = 0

    private successCount = 0
    private refusedStreamCount = 0

    /**
     * The maximum number of concurrent streams that can be carried by this connection. If
     * `allocations.size() < allocationLimit` then new streams can be created on this connection.
     */
    private allocationLimit = 1

    /** Current calls carried by this connection. */
    calls = new List<RealCall>()

    /** Timestamp when `allocations.size()` reached zero. Also assigned upon initial connection. */
    idleAtNs = Number.MAX_VALUE

    /**
     * Returns true if this is an HTTP/2 connection. Such connections can be used in multiple HTTP
     * requests simultaneously.
     */
    _isMultiplexed: boolean
    get isMultiplexed(){
        this._isMultiplexed = this.http2Connection !== null
        return this._isMultiplexed
    }

    /** Prevent further exchanges from being created on this connection. */
    // @Synchronized numberernal fun
    noNewExchanges() {
        this._noNewExchanges = true
    }

    getNoNewExchanges() {
       return this._noNewExchanges
    }


    /** Prevent this connection from being used for hosts other than the one in [route]. */
    // @Synchronized numberernal fun
    noCoalescedConnections() {
        this._noCoalescedConnections = true
    }

    // @Synchronized numberernal fun
    incrementSuccessCount() {
        this.successCount++
    }

    async connect(connectTimeout: number, readTimeout: number, writeTimeout: number, pingnumberervalMillis: number, connectionRetryEnabled: boolean, call: Call, eventListener: EventListener) {
        kotlinCheck(this._protocol === null || this._protocol === undefined, "already connected")
        let routeException: RouteException | null = null
        let connectionSpecs = this._route.address.connectionSpecs
        let connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs)

        if (this._route.address.sslSocketFactory === null || this._route.address.sslSocketFactory === undefined) {
            if (!connectionSpecs.includes(ConnectionSpec.CLEARTEXT)){
                throw new RouteException(new UnknownServiceException("CLEARTEXT communication not enabled for client"))
            }
            let host = this._route.address.url.host
            if (!networkSecurity.isCleartextPermittedByHostName(host)) { // !Platform.get().isCleartextTrafficPermitted(host) 检查当前平台是否支持明文http
                throw new RouteException(new UnknownServiceException(`CLEARTEXT communication to ${host} not permitted by network security policy`))
            }
        } else {
            // HTTPS 的 HTTP/2 必须通过 ALPN（Application-Layer Protocol Negotiation） 在 TLS 握手期间协商
            // 直接使用 H2_PRIOR_KNOWLEDGE 会跳过 ALPN，导致协议不匹配
            if (this._route.address.protocols.includes(Protocol.H2_PRIOR_KNOWLEDGE)) {
                throw new RouteException(new UnknownServiceException("H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"))
            }
        }

        while (true) {
            try {
                // 用于 HTTPS 通过 HTTP 代理的场景（CONNECT 隧道）
                if (this._route.requiresTunnel()) {
                    await this.connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
                    if (this.rawSocket === null || this.rawSocket === undefined) {
                        // We were unable to connect the tunnel but properly closed down our resources.
                        break
                    }
                }
                // 此处做了改造; 因当前鸿蒙tcpSocket升级tlsSocket会耗时500ms,故改造此逻辑
                await this.establishProtocol(connectionSpecSelector, pingnumberervalMillis, call, eventListener,connectTimeout, readTimeout)
                eventListener.connectEnd(call, this._route.socketAddress, this._route.proxy, this._protocol)
                break
            } catch (e) {
                console.error(`okhttp :: RealConnection.connect caught error:`, e, "type:", typeof e, "instanceof Error:", e instanceof Error)
                await this._socket?.close()
                await this.rawSocket?.close()
                this._socket = null
                this.rawSocket = null
                this.source = null
                this.sink = null
                this._handshake = null
                this._protocol = null
                // this.http2Connection = null
                this.allocationLimit = 1

                eventListener.connectFailed(call, this._route.socketAddress, this._route.proxy, null, e)

                if (routeException === null || routeException === undefined) {
                    routeException = new RouteException(e)
                } else {
                    routeException.addConnectException(e)
                }

                if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
                    throw routeException
                }
            }
        }

        if (this._route.requiresTunnel() && (this.rawSocket === null || this.rawSocket === undefined)) {
            throw new RouteException(new ProtocolException(`Too many tunnel connections attempted: ${RealConnection.MAX_TUNNEL_ATTEMPTS}`))
        }

        this.idleAtNs = Date.now() * 1000000 // 转换为纳秒
    }

    /**
     * Does all the work to build an HTTPS connection over a proxy tunnel. The catch here is that a
     * proxy server can issue an auth challenge and then close the connection.
     */
    // @Throws(IOException::class)
    private async connectTunnel(connectTimeout: number, readTimeout: number, writeTimeout: number, call: Call, eventListener: EventListener) {
        let tunnelRequest: Request = this.createTunnelRequest()
        let url = tunnelRequest.url
        for (let i = 0; i < RealConnection.MAX_TUNNEL_ATTEMPTS; i++){
            await this.connectSocket(connectTimeout, readTimeout, call, eventListener)
            tunnelRequest = await this.createTunnel(readTimeout, writeTimeout, tunnelRequest, url)
            if (tunnelRequest === null || tunnelRequest === undefined) {
                break
            }// Tunnel successfully created.

            // The proxy decided to close the connection after an auth challenge. We need to create a new
            // connection, but this time with the auth credentials.
            await this.rawSocket?.close()
            this.rawSocket = null
            this.sink = null
            this.source = null
            eventListener.connectEnd(call, this._route.socketAddress, this._route.proxy, null)
        }
    }

    /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
    // @Throws(IOException::class)
    private async connectSocket(connectTimeout: number, readTimeout: number, call: Call, eventListener: EventListener){
        let proxy = this._route.proxy
        let address = this._route.address
        let options = new TCPConnectOptions()
        if (proxy.proxyType === ProxyType.DIRECT || proxy.proxyType === ProxyType.HTTP){
            options.proxy.type = socket.ProxyTypes.NONE
        }else{
            options.proxy.type = socket.ProxyTypes.SOCKS5
            options.proxy.address = this._route.proxy.address
        }

        // 由于鸿蒙的socks代理是在connect阶段设置option,所以这里不需要单独创建一个socks代理socket
        let rawSocket: socket.TCPSocket = address.socketFactory.createSocket()
        this.rawSocket = rawSocket
        eventListener.connectStart(call, this._route.socketAddress, proxy)
        // Socket 读取数据时的阻塞超时时间。 暂时无法设置
        // rawSocket.soTimeout = readTimeout

        // 设置连接超时
        options.timeout = connectTimeout
        options.address.address = this._route.socketAddress.address
        options.address.port = this._route.socketAddress.port
        // Platform.get().connectSocket(rawSocket, this._route.socketAddress, connectTimeout)
        // 鸿蒙特有连接前需绑定本地端口
        // 特别注意: 若remote为ipv6地址,bind也需要连接本地ipv6地址:: 后续需加入该逻辑
        console.log("okhttp test log :: " , "Socket.bind : 开始")
        await connection.getDefaultNet().then(async (netHandle)=>{
            await connection.getConnectionProperties(netHandle).then(async (data: connection.ConnectionProperties)=>{
                let ip = data.linkAddresses[0].address.address
                await rawSocket.bind({ address: ip}).then(()=>{
                }).catch((err)=>{
                    throw new SocketException(`Failed to bind to current port` + JSON.stringify(err))
                })
            }).catch(()=>{
                throw new SocketException(`Failed to get ConnectionProperties`)
            })
        }).catch(()=>{
            throw new SocketException(`Failed to get NetHandle`)
        })

        rawSocket.on('error', (err: BusinessError) => {
            // 根据错误码创建对应的异常类型
            const socketError = createSocketException(err, 'TCP')
            this._lastSocketError = socketError
            console.error("okhttp :: TCP Socket.error : " + JSON.stringify(err) + ", exception: " + socketError.name)

            // 添加清理连接池逻辑
            rawSocket.off('message');
            rawSocket.off('close')
            rawSocket.off('error')
            rawSocket.off('connect')
            console.log("okhttp :: connectionPool : 清理当前connection (TCP error)")
            this.connectionPool.delete(this)
            this.source?.SocketClose()
            this._socketClosed = true
        })

        rawSocket.on('close', () => {
            // 添加清理连接池逻辑 // 主动清理和被动清理都会进入到该监听进行删除连接池功能
            rawSocket.off('message');
            rawSocket.off('close')
            rawSocket.off('error')
            rawSocket.off('connect')
            console.log("okhttp :: connectionPool : 清理当前connection (TCP close)")
            this.connectionPool.delete(this)
            this.source?.SocketClose()
            this._socketClosed = true
        })

        console.log("okhttp test log :: " , "Socket.connect : 开始")
        await rawSocket.connect(options).catch((e: BusinessError)=>{
            // 打印完整错误信息用于调试
            console.error(`okhttp :: Socket.connect failed - code: ${e?.code}, message: ${e?.message}, error: ${JSON.stringify(e)}`)
            const socketEx = new SocketException(`Failed to connect to ${this._route.socketAddress}, code: ${e?.code}, message: ${e?.message}`)
            console.error(`okhttp :: Socket.connect throwing SocketException:`, socketEx, socketEx?.message)
            throw socketEx
        })

        // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
        // More details:
        // https://github.com/square/okhttp/issues/3245
        // https://android-review.googlesource.com/#/c/271775/
        try {
            this.source = new BufferedSource(rawSocket)
            this.sink = new BufferedSink(rawSocket)
        } catch (e) {
            if (e.message == RealConnection.NPE_THROW_WITH_NULL) {
                throw new IOException(e.message)
            }else{
                throw e
            }
        }
    }

    // @Throws(IOException::class)
    private async establishProtocol(connectionSpecSelector: ConnectionSpecSelector, pingnumberervalMillis: number, call: Call, eventListener: EventListener, connectTimeout: number, readTimeout: number) {
        if (this._route.address.sslSocketFactory === null || this._route.address.sslSocketFactory === undefined) {
            if(this._route.address.protocols.includes(Protocol.H2_PRIOR_KNOWLEDGE)){
                this._socket = this.rawSocket
                this._protocol = Protocol.H2_PRIOR_KNOWLEDGE
                // startHttp2(pingnumberervalMillis)
                return
            }
            await this.connectSocket(connectTimeout, readTimeout, call, eventListener)
            this._socket = this.rawSocket
            this._protocol = Protocol.HTTP_1_1
            return
        }
        eventListener.secureConnectStart(call)
        await this.connectTls(connectionSpecSelector)
        eventListener.secureConnectEnd(call, this._handshake)

        if (this._protocol === Protocol.HTTP_2) {
            throw new ProtocolException(`http2 is not supported at present`)
            // startHttp2(pingnumberervalMillis)
        }
    }

    // 鸿蒙http2暂时无法实现,暂时不做
    // @Throws(IOException::class)
    // private startHttp2(pingnumberervalMillis: number) {
    //     // val socket = this.socket!!
    //     // val source = this.source!!
    //     // val sink = this.sink!!
    //     // socket.soTimeout = 0 // HTTP/2 connection timeouts are set per-stream.
    //     // val http2Connection = Http2Connection.Builder(client = true, taskRunner = TaskRunner.INSTANCE)
    //     //     .socket(socket, route.address.url.host, source, sink)
    //     //     .listener(this)
    //     //     .pingnumberervalMillis(pingnumberervalMillis)
    //     //     .build()
    //     // this.http2Connection = http2Connection
    //     // this.allocationLimit = Http2Connection.DEFAULT_SETTINGS.getMaxConcurrentStreams()
    //     // http2Connection.start()
    // }

    // @Throws(IOException::class)
    private async connectTls(connectionSpecSelector: ConnectionSpecSelector) {
        let address = this._route.address
        let sslSocketFactory = address.sslSocketFactory
        let success = false
        let sslSocket: SSLSocket | null = null
        try {
            // Create the wrapper over the connected socket.
            // 判断是否为connect隧道 是的话则升级tls
            if (this._route.requiresTunnel()){
                sslSocket = sslSocketFactory.createSocket(this.rawSocket)
            }else{
                sslSocket = sslSocketFactory.createSocket()
                console.log("okhttp test log :: " , "Socket.bind : 开始")
                await connection.getDefaultNet().then(async (netHandle)=>{
                    await connection.getConnectionProperties(netHandle).then(async (data: connection.ConnectionProperties)=>{
                        let ip = data.linkAddresses[0].address.address
                        await sslSocket.bind({ address: ip}).then(()=>{
                        }).catch((err)=>{
                            throw new SocketException(`Failed to bind to current port` + JSON.stringify(err))
                        })
                    }).catch(()=>{
                        throw new SocketException(`Failed to get ConnectionProperties`)
                    })
                }).catch(()=>{
                    throw new SocketException(`Failed to get NetHandle`)
                })
            }

            sslSocket.on('error', (err: BusinessError) => {
                // 某些 SSL 错误码是非阻塞 I/O 的正常状态，可以忽略
                // arkts socket非阻塞错误会返回errorNumber属性; 有该属性时忽略此error
                if ((err as ESObject).errorNumber !== undefined){
                    return
                }
                const errorNumber = err?.code ?? 0
                if (errorNumber === SocketErrorCode.SSL_ERROR_WANT_READ ||
                    errorNumber === SocketErrorCode.SSL_ERROR_WANT_WRITE) {
                    console.log("okhttp :: TLS Socket pending I/O, errorNumber: " + errorNumber)
                    return
                }

                // 根据错误码创建对应的异常类型
                const socketError = createSocketException(err, 'TLS')
                this._lastSocketError = socketError
                console.error("okhttp :: TLS Socket.error : " + JSON.stringify(err) + ", exception: " + socketError.name)

                // 添加清理连接池逻辑
                sslSocket.off('message');
                sslSocket.off('close')
                sslSocket.off('error')
                sslSocket.off('connect')
                console.log("okhttp :: connectionPool : 清理当前connection (TLS error)")
                this.connectionPool.delete(this)
                this.source?.SocketClose()
                this._socketClosed = true
            })

            sslSocket.on('close', () => {
                // 添加清理连接池逻辑
                sslSocket.off('message');
                sslSocket.off('close')
                sslSocket.off('error')
                sslSocket.off('connect')
                console.log("okhttp :: connectionPool : 清理当前connection (TLS close)")
                this.connectionPool.delete(this)
                this.source?.SocketClose()
                this._socketClosed = true
            })

            let proxyOptions = new ProxyOptions()
            if (this._route.proxy.proxyType === ProxyType.DIRECT || this._route.proxy.proxyType === ProxyType.HTTP){
                proxyOptions.type = socket.ProxyTypes.NONE
            }else{
                proxyOptions.type = socket.ProxyTypes.SOCKS5
                proxyOptions.address = this._route.proxy.address
            }
            let netAddress = new NetAddress()
            netAddress.address = this._route.socketAddress.address
            netAddress.port = this._route.socketAddress.port

            // 将ssl options生成
            let tlsOptions = new TLSConnectOptions(netAddress, proxyOptions, sslSocket, sslSocketFactory)
            // sslSocketFactory!!.createSocket(rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket
            // Configure the socket's ciphers, TLS versions, and extensions.
            let connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)

            address.protocols.forEach((protocol) => {tlsOptions.ALPNProtocols?.push(protocol.toString())})
            // 配置tls拓展已被废弃
            // if (connectionSpec.supportsTlsExtensions) {
            //     sslSocket.configureTlsExtensions(address.url.host, address.protocols)
            // }

            // Force handshake. This can throw!
            await sslSocket.startHandshake(tlsOptions)

            // block for session establishment
            let sslSocketSession = sslSocket.session
            let unverifiedHandshake = await sslSocketSession.handshake()

            // 因鸿蒙socket不支持传入自定义证书校验; 此处添加自定义证书校验逻辑
            try{
                await address.sslSocketFactory.tm.checkServerTrusted(unverifiedHandshake.peerCertificates)
            }catch (e) {
                if (e instanceof CertificateException){
                    throw e
                }
                throw new CertificateException('check server certificate fail')
            }

            // Verify that the socket's certificates are acceptable for the target host.
            if (!address.hostnameVerifier.verify(address.url.host, sslSocketSession)) {
                let peerCertificates = unverifiedHandshake.peerCertificates
                if (peerCertificates !== null) {
                    let realCert = await cert.createX509Cert(peerCertificates)
                    let subjectDN = realCert.getSubjectName()
                    throw new SSLPeerUnverifiedException(`|Hostname ${address.url.host} not verified:`+
                    `|    certificate: ${CertificatePinner.pin(realCert)}`+
                    `|    DN: ${buffer.from(subjectDN.data).toString()}`+
                    `|    subjectAltNames: ${OkHostnameVerifier.allSubjectAltNames(realCert)}`)
                } else {
                    throw new SSLPeerUnverifiedException(`|Hostname ${address.url.host} not verified (no certificates)`)
                }
            }

            let certificatePinner = address.certificatePinner

            this._handshake = new Handshake(unverifiedHandshake.tlsVersion,
                unverifiedHandshake.cipherSuite,
                unverifiedHandshake.localCertificates,
                ()=>unverifiedHandshake.peerCertificates)

            // Check that the certificate pinner is satisfied by the certificates presented.
            await certificatePinner.check(address.url.host, this._handshake.peerCertificates)

            // Success! Save the handshake and the ALPN protocol.\
            // let maybeProtocol = null
            // if (connectionSpec.supportsTlsExtensions){
            //     // maybeProtocol = Platform.get().getSelectedProtocol(sslSocket)
            // }

            this._socket = sslSocket
            // 刷新source和sink ,因为tcp升级为tls
            this.source = new BufferedSource(sslSocket)
            this.sink = new BufferedSink(null,sslSocket)
            // if (maybeProtocol != null){
            //     // this._protocol = Protocol.get(maybeProtocol)
            // }else{
            // 当前只支持http1.1
            this._protocol = Protocol.HTTP_1_1
            // }
            success = true
        } finally {
            // Android低版本清除ALPN使用
            // if (sslSocket != null) {
                // Platform.get().afterHandshake(sslSocket)
            // }
            if (!success) {
                await sslSocket?.close()
            }
        }
    }

    /**
     * To make an HTTPS connection over an HTTP proxy, send an unencrypted CONNECT request to create
     * the proxy connection. This may need to be retried if the proxy requires authorization.
     */
    // @Throws(IOException::class)
    private async createTunnel(readTimeout: number, writeTimeout: number, tunnelRequest: Request, url: HttpUrl): Promise<Request> | null {
        let nextRequest = tunnelRequest
        // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
        let requestLine = `CONNECT ${toHostHeader(url, true)} HTTP/1.1`
        while (true) {
            let source = this.source
            let sink = this.sink
            let tunnelCodec = new Http1ExchangeCodec(null, this, source, sink)
            // 设置读写超时; 暂不实现
            // source.timeout().timeout(readTimeout.toLong(), MILLISECONDS)
            // sink.timeout().timeout(writeTimeout.toLong(), MILLISECONDS)
            tunnelCodec.writeRequest(nextRequest.headers, requestLine)
            await tunnelCodec.finishRequest()
            let response: Response = (await tunnelCodec.readResponseHeaders(false))
                .request(nextRequest)
                .build()
            tunnelCodec.skipConnectBody(response)
            if (response.code === http.ResponseCode.OK){
                if (!source.buffer().isEmpty || !sink?.isEmpty) {
                    throw new IOException("TLS tunnel buffered too many bytes!")
                }
                return null
            }else if (response.code === http.ResponseCode.PROXY_AUTH){
                nextRequest = this._route.address.proxyAuthenticator.authenticate(this._route, response)
                if (nextRequest === null || nextRequest === undefined) {
                    throw new IOException("Failed to authenticate with proxy")
                }
                if (response.header("Connection")?.toLowerCase() === "close"){
                    return nextRequest
                }
            }else{
                throw new IOException(`Unexpected response code for CONNECT: ${response.code}`)
            }
        }
    }

    /**
     * Returns a request that creates a TLS tunnel via an HTTP proxy. Everything in the tunnel request
     * is sent unencrypted to the proxy server, so tunnels include only the minimum set of headers.
     * This avoids sending potentially sensitive data like HTTP cookies to the proxy unencrypted.
     *
     * In order to support preemptive authentication we pass a fake "Auth Failed" response to the
     * authenticator. This gives the authenticator the option to customize the CONNECT request. It can
     * decline to do so by returning null, in which case OkHttp will use it as-is.
     */
    // @Throws(IOException::class)
    private createTunnelRequest(): Request {
        let proxyConnectRequest = new Request.Builder()
            .url(this._route.address.url)
            .method("CONNECT", null)
            .header("Host", toHostHeader(this._route.address.url, true))
            .header("Proxy-Connection", "Keep-Alive") // For HTTP/1.0 proxies like Squid.
            .header("User-Agent", userAgent)
            .build()

        let fakeAuthChallengeResponse = new Response.Builder()
            .request(proxyConnectRequest)
            .protocol(Protocol.HTTP_1_1)
            .code(http.ResponseCode.PROXY_AUTH)
            .message("Preemptive Authenticate")
            .body(EMPTY_RESPONSE)
            .sentRequestAtMillis(-1)
            .receivedResponseAtMillis(-1)
            .header("Proxy-Authenticate", "OkHttp-Preemptive")
            .build()

        let authenticatedRequest = this._route.address.proxyAuthenticator
            .authenticate(this._route, fakeAuthChallengeResponse)

        return authenticatedRequest ?? proxyConnectRequest
    }

    /**
     * Returns true if this connection can carry a stream allocation to `address`. If non-null
     * `route` is the resolved route for a connection.
     */
    async isEligible(address: Address, routes?: List<Route>): Promise<boolean> {
        // assertThreadHoldsLock()
        console.log("test:: isEligible start - calls:", this.calls.length.toString(), "allocationLimit:", this.allocationLimit.toString(), "noNewExchanges:", this.getNoNewExchanges())
        // If this connection is not accepting new exchanges, we're done.
        if (this.calls.length >= this.allocationLimit || this.getNoNewExchanges()) {
            console.log("test:: isEligible failed - allocationLimit exceeded or noNewExchanges")
            return false
        }

        // If the non-host fields of the address don't overlap, we're done.
        if (!this._route.address.equalsNonHost(address)) {
            console.log("test:: isEligible failed - equalsNonHost returned false")
            return false
        }

        // If the host exactly matches, we're done: this connection can carry the address.
        if (address.url.host == this.route().address.url.host) {
            console.log("test:: isEligible success - host match")
            return true // This connection is a perfect match.
        }

        // At this ponumber we don't have a hostname match. But we still be able to carry the request if
        // our connection coalescing requirements are met. See also:
        // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
        // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/

        // 1. This connection must be HTTP/2.
        // if (this.http2Connection == null) return false

        // 2. The routes must share an IP address.
        if (routes === null || routes === undefined || !this.routeMatchesAny(routes)) return false

        // 3. This connection's server certificate's must cover the new host.
        if (address.hostnameVerifier !== OkHostnameVerifier.INSTANCE) return false
        if (!await this.supportsUrl(address.url)) return false

        // 4. Certificate pinning must match the host.
        try {
            await address.certificatePinner.check(address.url.host, this.handshake().peerCertificates)
        } catch (_) {
            return false
        }

        return true // The caller's address can be carried by this connection.
    }

    /**
     * Returns true if this connection's route has the same address as any of [candidates]. This
     * requires us to have a DNS address for both hosts, which only happens after route planning. We
     * can't coalesce connections that use a proxy, since proxies don't tell us the origin server's IP
     * address.
     */
    private routeMatchesAny(candidates: List<Route>): boolean {
        for (const candidate of candidates) {
            if (candidate.proxy.proxyType === ProxyType.DIRECT &&
                this._route.proxy.proxyType === ProxyType.DIRECT &&
                this._route.socketAddress.equals(candidate.socketAddress)){
                return true
            }
        }
        return false
    }

    private async supportsUrl(url: HttpUrl): Promise<boolean> {
        // assertThreadHoldsLock()

        let routeUrl = this._route.address.url

        if (url.port != routeUrl.port) {
            return false // Port mismatch.
        }

        if (url.host == routeUrl.host) {
            return true // Host match. The URL is supported.
        }

        // We have a host mismatch. But if the certificate matches, we're still good.
        return !this._noCoalescedConnections && this._handshake !== null && this._handshake !== undefined && await this.certificateSupportHost(url, this._handshake)
    }

    private async certificateSupportHost(url: HttpUrl, handshake: Handshake): Promise<boolean> {
        let peerCertificates = handshake.peerCertificates
        return peerCertificates !== null && peerCertificates !== undefined && await OkHostnameVerifier.INSTANCE.verifyCertificate(url.host, peerCertificates)
    }

    // @Throws(SocketException::class)
    newCodec(client: OkHttpClient, chain?: any): ExchangeCodec { // RealnumbererceptorChain
        // let socket = this._socket!!
        let source = this.source
        let sink = this.sink
        // http2 暂不实现
        // let http2Connection = this.http2Connection
        // if (http2Connection != null) {
        //     return new Http2ExchangeCodec(client, this, chain, http2Connection)
            // return null
        // }else{
            // 鸿蒙无法设置
            // socket.soTimeout = chain.readTimeoutMillis()
            // 暂不实现,后续优化
            // source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)
            // sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)
        return new Http1ExchangeCodec(client, this, source, sink)
        // }
    }

    // @Throws(SocketException::class)
    newWebSocketStreams(exchange: Exchange): any { //RealWebSocket.Streams {
        // val socket = this.socket!!
        // val source = this.source!!
        // val sink = this.sink!!
        //
        // socket.soTimeout = 0
        // noNewExchanges()
        // return object : RealWebSocket.Streams(true, source, sink) {
        //     override fun close() {
        //         exchange.bodyComplete<IOException?>(-1L, responseDone = true, requestDone = true, e = null)
        //     }
        // }
        return null
    }

    route(): Route {
        return this._route
    }

    async cancel() {
        // Close the raw socket so we don't end up doing synchronous I/O.
        await this.rawSocket?.close()
        this.source?.SocketClose()
    }

    get socket(){
        return this._socket
    }

    set socket(sock: socket.TCPSocket | socket.TLSSocket){
        this._socket = sock
    }

    /** Returns true if this connection is ready to host new streams. */
    async isHealthy(doExtensiveChecks: boolean): Promise<boolean> {
        // assertThreadDoesntHoldLock()
        let nowNs = Date.now() * 1000000 // Convert to nanoseconds to match idleAtNs
        let rawSocket = this.rawSocket
        let socket = this._socket
        
        // 首先检查 socket 是否已通过事件回调标记为关闭
        // 这是最快速的检查，因为事件回调会设置此标志
        if (this._socketClosed) {
            console.log("okhttp test log :: isHealthy - returning false (_socketClosed is true)")
            return false
        }
        
        // 判断是否是 TLS 连接（socket !== rawSocket 表示 TLS 已升级）
        // 对于 TLS 连接，底层 rawSocket 的 getState() 可能返回 isClose:true，这是正常的
        // 因为 TLS socket 接管了连接，我们只需检查 TLS socket 的状态
        const isTlsConnection = socket !== rawSocket && socket !== null && socket !== undefined
        
        let socketState = await socket?.getState()
        console.log("okhttp test log :: isHealthy - isTlsConnection:", isTlsConnection,
            "socketState:", JSON.stringify(socketState), "_socketClosed:", this._socketClosed)
        
        // 对于 TLS 连接，只检查 TLS socket 的状态
        // 对于非 TLS 连接，检查 rawSocket（此时 socket === rawSocket）
        if (isTlsConnection) {
            // TLS 连接：只检查 TLS socket 的状态
            if (socketState?.isClose) {
                console.log("okhttp test log :: isHealthy - returning false (TLS socket isClose)")
                return false
            }
            if (!socketState?.isConnected) {
                console.log("okhttp test log :: isHealthy - returning false (TLS socket not connected)")
                return false
            }
        } else {
            // 非 TLS 连接：检查 rawSocket 的状态
            let rawSocketState = await rawSocket?.getState()
            console.log("okhttp test log :: isHealthy - rawSocketState:", JSON.stringify(rawSocketState))
            if (rawSocketState?.isClose) {
                console.log("okhttp test log :: isHealthy - returning false (raw socket isClose)")
                return false
            }
            if (!rawSocketState?.isConnected) {
                console.log("okhttp test log :: isHealthy - returning false (raw socket not connected)")
                return false
            }
        }

        // 暂不实现http2
        // let http2Connection = this.http2Connection
        // if (http2Connection != null) {
        //     return http2Connection.isHealthy(nowNs)
        // }

        let idleDurationNs = nowNs - this.idleAtNs // synchronized(this) {  }
        console.log("okhttp test log :: isHealthy - idleDurationNs:", idleDurationNs, 
            "threshold:", RealConnection.IDLE_CONNECTION_HEALTHY_NS, "doExtensiveChecks:", doExtensiveChecks)
        
        // 如果空闲时间超过阈值，需要做更深入的检查
        // 对于刚变空闲的连接，我们信任 socket 状态检查的结果
        if (idleDurationNs >= RealConnection.IDLE_CONNECTION_HEALTHY_NS && doExtensiveChecks) {
            // 再次检查 _socketClosed（可能在 getState() 期间被设置）
            if (this._socketClosed) {
                console.log("okhttp test log :: isHealthy - returning false (socketClosed after idle check)")
                return false
            }
        }

        console.log("okhttp test log :: isHealthy - returning true")
        return true
    }

    /** Refuse incoming streams. */
    // @Throws(IOException::class)
    onStream(stream: any) {
        // stream.close(ErrorCode.REFUSED_STREAM, null)
    }

    /** When settings are received, adjust the allocation limit. */
    // @Synchronized override fun
    // onSettings(connection: Http2Connection, settings: Settings) {
    //     // allocationLimit = settings.getMaxConcurrentStreams()
    // }

    handshake(): Handshake | null{
        return this._handshake
    }

    /** Track a bad route in the route database. Other routes will be attempted first. */
    connectFailed(client: OkHttpClient, failedRoute: Route, failure: Error) {
        // Tell the proxy selector when we fail to connect on a fresh connection.
        if (failedRoute.proxy.proxyType !== ProxyType.DIRECT) {
            let address = failedRoute.address
            address.proxySelector.connectFailed(address.url.toUri(), failedRoute.proxy.address, failure)
        }

        client.routeDatabase.failed(failedRoute)
    }

    /**
     * Track a failure using this connection. This may prevent both the connection and its route from
     * being used for future exchanges.
     */
    // @Synchronized numberernal fun
    trackFailure(call: RealCall, e?: Error) {
        if (e instanceof  StreamResetException) {
            if (e.errorCode == ErrorCode.REFUSED_STREAM){
                // Stop using this connection on the 2nd REFUSED_STREAM error.
                this.refusedStreamCount++
                if (this.refusedStreamCount > 1) {
                    this._noNewExchanges = true
                    this.routeFailureCount++
                }
            }else if (e.errorCode == ErrorCode.CANCEL && call.isCanceled()){
                // Permit any number of CANCEL errors on locally-canceled calls.
            }else {
                // Everything else wants a fresh connection.
                this._noNewExchanges = true
                this.routeFailureCount++
            }
        }else if (!this._isMultiplexed || e instanceof ConnectionShutdownException) {
            this._noNewExchanges = true

            // If this route hasn't completed a call, avoid it for new connections.
            if (this.successCount === 0) {
                if (e !== null && e !== undefined) {
                    this.connectFailed(call.client, this._route, e)
                }
                this.routeFailureCount++
            }
        }
    }

    protocol(): Protocol{
        return this._protocol
    }

    toString(): string {
        const cipherSuite = this._handshake?.cipherSuite ?? "none"
        return `Connection{${this._route.address.url.host}:${this._route.address.url.port},` +
            ` proxy=${this._route.proxy}` +
            ` hostAddress=${this._route.socketAddress}` +
            ` cipherSuite=${cipherSuite}` +
            ` protocol=${this._protocol}}`
    }

    newTestConnection(connectionPool: RealConnectionPool, route: Route, socket: socket.TCPSocket | socket.TLSSocket | null, idleAtNs: number): RealConnection {
        let result = new RealConnection(connectionPool, route)
        result.socket = socket
        result.idleAtNs = idleAtNs
        return result
    }
}
